/**
* Created by chenly on 12/29/23.
* Description
**/

#ifndef SIMNOW_TEST_TRADERSPIIMPL_H
#define SIMNOW_TEST_TRADERSPIIMPL_H
//c++ headers
#include <iostream>
#include <unordered_map>
#include <cstring>
#include <fstream>
#include <memory>
#include <thread>
#include <csignal>
#include <vector>
#include <string>
#include <unordered_map>
#include <list>
#include <mutex>
#include <condition_variable>
#include <map>
//ctp headers
#include "ThostFtdcMdApi.h"
#include "ThostFtdcTraderApi.h"
#include "ThostFtdcUserApiStruct.h"
#include "lolog/include/Lolog.h"

namespace lyctp {
    using namespace std;
    using namespace lolog;
    class TraderSpiImpl : public CThostFtdcTraderSpi {

        CThostFtdcTraderApi *m_trader_api;
        TThostFtdcBrokerIDType m_broker_id;
        TThostFtdcInvestorIDType m_invester_id;
        TThostFtdcPasswordType m_password;
        long m_req_id = 0;
        bool m_is_end = false;
        bool m_has_error = false;
        unordered_map<string,CThostFtdcInstrumentField> m_all_instruments;
    public:
        TraderSpiImpl(const string& broker_id,
                      const string& invester_id,
                      const string& password) {
            strcpy(m_broker_id, broker_id.data());
            strcpy(m_invester_id, invester_id.data());
            strcpy(m_password, password.data());
        }

        ~TraderSpiImpl() {
            m_trader_api->Release();
        }

        bool is_end() {
            return m_is_end;
        }

        bool has_error() {
            return m_has_error;
        }

        unordered_map<string,CThostFtdcInstrumentField>& get_all_instruments() {
            return m_all_instruments;
        }

        bool connect(char *front_addr) {

            m_trader_api = CThostFtdcTraderApi::CreateFtdcTraderApi();
            if (!m_trader_api) {
                LODEBUG() << "ctp create traderapi failed";
                return false;
            }

            m_trader_api->RegisterSpi(this);

            LODEBUG() << "Trade FrontAddress=" << front_addr << endl;

            m_trader_api->SubscribePublicTopic(THOST_TERT_QUICK);
            m_trader_api->SubscribePrivateTopic(THOST_TERT_QUICK);
            m_trader_api->RegisterFront(front_addr);
            m_trader_api->Init(); //调用Init，开始连接
            return true;
        }

        virtual void OnFrontConnected() {
            CThostFtdcReqUserLoginField reqUserLogin;
            strcpy(reqUserLogin.BrokerID, m_broker_id);
            strcpy(reqUserLogin.UserID, m_invester_id);
            strcpy(reqUserLogin.Password, m_password);
            LODEBUG() << "reqUserLogin.BrokerID:" << reqUserLogin.BrokerID << endl;
            LODEBUG() << "reqUserLogin.UserID:" << reqUserLogin.UserID << endl;
            LODEBUG() << "reqUserLogin.Password:" << reqUserLogin.Password << endl;
            int iResult = m_trader_api->ReqUserLogin(&reqUserLogin, ++m_req_id);
            LODEBUG() << "--->>> ReqUserLogin: " << ((iResult == 0) ? "Sucess" : "Failed") << endl;
        }


        virtual void OnFrontDisconnected(int nReason) {
            // 当发生这个情况后，API会自动重新连接，客户端可不做处理
            printf("FrontDisconnected.\n");
        }

        virtual void ReqSettlementInfoConfirm() {

            CThostFtdcSettlementInfoConfirmField req;
            memset(&req, 0, sizeof(req));
            strcpy(req.BrokerID, m_broker_id);
            strcpy(req.InvestorID, m_invester_id);
            int ret = m_trader_api->ReqSettlementInfoConfirm(&req, ++m_req_id);
            LODEBUG() << "--->>> SettlementInfoConfirm: " << ((ret == 0) ? "Success" : "Failed") << endl;
            ReqQryInstrument();
        }

        virtual void OnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin,
                                    CThostFtdcRspInfoField *pRspInfo, int nRequestID,
                                    bool bIsLast) {
            if (pRspInfo->ErrorID != 0) {
                LODEBUG() << "Failed to login, errorcode=" << pRspInfo->ErrorID << "ErrorMsg=" << pRspInfo->ErrorMsg << endl;

                m_is_end = true;
                m_has_error = true;

            } else {
                ReqSettlementInfoConfirm();
            }
        }


        void ReqQryInstrument() {
            CThostFtdcQryInstrumentField req;
            memset(&req, 0, sizeof(req));

            int iResult = m_trader_api->ReqQryInstrument(&req, ++m_req_id);
            LODEBUG() << "--->>> QryInstrument: " << ((iResult == 0) ? "Success" : "Failed") << endl;
        }


        void OnRspQryInstrument(CThostFtdcInstrumentField *pInstrument,
                                CThostFtdcRspInfoField *pRspInfo, int nRequestID,
                                bool bIsLast) {
            string instrument_id((char *) &(pInstrument->InstrumentID));
            m_all_instruments.emplace(std::make_pair(instrument_id, *pInstrument));
            if (bIsLast == true) {
                m_is_end = true;
            }
        }
    };

    inline bool get_all_instruments(const string &trade_front_addr, const string &broker_id,
                                    const string &invester_id,
                                    const string &password,
                                    unordered_map<string, CThostFtdcInstrumentField> &out_instruments) {
        auto start = std::chrono::high_resolution_clock::now();

        TraderSpiImpl trader_spi_impl(broker_id, invester_id, password);
        if (!trader_spi_impl.connect(const_cast<char *>(trade_front_addr.data()))) {
            return false;
        }

        while (!trader_spi_impl.is_end() && !trader_spi_impl.has_error()) {
            this_thread::sleep_for(chrono::milliseconds(100));
        }
        auto elapsed = std::chrono::high_resolution_clock::now() - start;
        int time_cost = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();

        out_instruments.swap(trader_spi_impl.get_all_instruments());
        LODEBUG() << "get_all_instruments size=" << out_instruments.size() << " time cost =" << time_cost << " ms" << endl;
        return trader_spi_impl.has_error();
    }

}


#endif //SIMNOW_TEST_TRADERSPIIMPL_H
